package com.jwm.client;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.jwm.client.soft.*;
import com.jwm.model.*;
import com.jwm.model.ServiceVo;
import com.jwm.util.DateUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.stereotype.Component;
import oshi.util.Util;

import javax.annotation.Resource;
import java.io.File;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * @author 崔洁
 */
@Component
@EnableScheduling
public class ClientTimeTask implements SchedulingConfigurer {
    private static final Logger logger = LoggerFactory.getLogger(ClientTimeTask.class);
    @Resource
    private ClientConfig clientConfig;
    @Resource
    private NginxConfigEditor nginxConfigEditor;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(
                new ScheduledThreadPoolExecutor(Runtime.getRuntime().availableProcessors() * 2)
        );
        String clientName = clientConfig.getName();
        if (clientConfig.getTimeHeartInterval() > 0) {
            taskRegistrar.addTriggerTask(
                    () -> {
                        logger.warn("客户端 -> 心跳执行-开始");
                        if (!AdminCheck.isUseAdmin()) {
                            logger.info("客户端 -> 请以管理员身份运行! ");
                        }
                        heartTask(clientName);
                        logger.info("客户端 -> 心跳执行-结束");
                    },
                    //triggerContext -> new CronTrigger("0/" + clientConfig.getTimeHeartInterval() + " * * * * ?").nextExecutionTime(triggerContext)
                    triggerContext -> new PeriodicTrigger(clientConfig.getTimeHeartInterval()).nextExecutionTime(triggerContext)
            );
        }
        if (clientConfig.getTimeComputerInterval() > 0) {
            taskRegistrar.addTriggerTask(
                    () -> {
                        logger.info("客户端 -> 电脑信息执行-开始");
                        computerTask(clientName);
                        logger.info("客户端 -> 电脑信息执行-结束");
                    },
                    //triggerContext -> new CronTrigger("0/" + clientConfig.getTimeComputerInterval() + " * * * * ?").nextExecutionTime(triggerContext)
                    triggerContext -> new PeriodicTrigger(clientConfig.getTimeComputerInterval()).nextExecutionTime(triggerContext)
            );
        }
        if (clientConfig.getTimeInfoInterval() > 0) {
            ExecuteCommand exe = new ExecuteCommand();
            taskRegistrar.addTriggerTask(
                    () -> {
                        logger.info("客户端 -> 其他信息执行-开始");
                        infoTask(clientName, exe);
                        logger.info("客户端 -> 其他信息执行-结束");
                    },
                    //triggerContext -> new CronTrigger("0/" + clientConfig.getTimeInfoInterval() + " * * * * ?").nextExecutionTime(triggerContext)
                    triggerContext -> new PeriodicTrigger(clientConfig.getTimeInfoInterval()).nextExecutionTime(triggerContext)
            );
        }
    }

    public void heartTask(String clientName) {
        String post = HttpUtil.post(clientConfig.getRemoteUrl() + "/jwm/heartbeat", "name=" + clientName);
        if (StringUtils.isNotBlank(post)) {
            for (String cmd : post.split("\\^_\\^")) {
                CmdInfoVo cmdInfoVo = JSONUtil.toBean(cmd, CmdInfoVo.class);
                exe(cmdInfoVo);
            }
        }
    }

    public void computerTask(String clientName) {
        Computer computer = new Computer();
        computer.copyTo();
        HttpUtil.post(clientConfig.getRemoteUrl() + "/jwm/getComputer?name=" + clientName
                , JSONUtil.toJsonStr(computer));

        SysFiles sysFiles = new SysFiles();
        sysFiles.copyTo();
        HttpUtil.post(clientConfig.getRemoteUrl() + "/jwm/getSysFiles?name=" + clientName
                , JSONUtil.toJsonStr(sysFiles.getSysFiles()));
    }

    public void infoTask(String clientName, ExecuteCommand exe) {
        queryTomcats(clientName);

        queryJars(clientName);

        queryService(clientName, exe);

        queryBats(clientName, exe);
    }


    private void queryTomcats(String clientName) {
        List<TomcatVo> tomcatVos = new ArrayList<>();
        for (App appVo : clientConfig.getTomcatList()) {
            TomcatVo tomcatVo = new TomcatTool().getInfo(appVo);
            tomcatVos.add(tomcatVo);
        }
        HttpUtil.post(clientConfig.getRemoteUrl() + "/jwm/getTomcats?name=" + clientName
                , JSONUtil.toJsonStr(tomcatVos));
    }

    private void queryJars(String clientName) {
        List<JarVo> jarVos = new ArrayList<>();
        for (App appVo : clientConfig.getJarList()) {
            JarVo jarVo = new JarTool().getInfo(appVo);
            jarVos.add(jarVo);
        }
        HttpUtil.post(clientConfig.getRemoteUrl() + "/jwm/getJars?name=" + clientName
                , JSONUtil.toJsonStr(jarVos));
    }

    private void queryService(String clientName, ExecuteCommand exe) {
        List<ServiceVo> serviceVos = new ArrayList<>();
        for (App appVo : clientConfig.getServiceList()) {
            ServiceVo serviceVo = new ServiceVo();
            serviceVo.setName(appVo.getName());
            serviceVo.setTime(DateUtils.dateTimeNow());
            serviceVo.setState(exe.exeServiceQuery(appVo.getServerName()));
            serviceVos.add(serviceVo);
        }
        HttpUtil.post(clientConfig.getRemoteUrl() + "/jwm/getServices?name=" + clientName
                , JSONUtil.toJsonStr(serviceVos));
    }

    private void queryBats(String clientName, ExecuteCommand exe) {
        List<BatVo> batVos = new ArrayList<>();
        for (App appVo : clientConfig.getBatList()) {
            BatVo batVo = new BatVo();
            batVo.setName(appVo.getName());
            batVo.setServerName(appVo.getServerName());
            batVo.setTime(DateUtils.dateTimeNow());
            batVo.setState(exe.exeBatQuery(appVo.getServerName()));
            batVos.add(batVo);
        }
        HttpUtil.post(clientConfig.getRemoteUrl() + "/jwm/getBats?name=" + clientName
                , JSONUtil.toJsonStr(batVos));
    }

    private void exe(CmdInfoVo cmdInfoVo) {
        ExecuteCommand exe = new ExecuteCommand();
        String serverName = cmdInfoVo.getServerName();
        TypeEnum typeEnum = null;
        if (cmdInfoVo.getTypeEnum() != null) {
            typeEnum = TypeEnum.valueOfLabel(cmdInfoVo.getTypeEnum().getLabel());
        }
        CmdEnum cmdEnum = null;
        if (cmdInfoVo.getCmdEnum() != null) {
            cmdEnum = CmdEnum.valueOfLabel(cmdInfoVo.getCmdEnum().getLabel());
        }
        switch (typeEnum) {
            case tomcat:
                exeTomcat(cmdInfoVo, exe, serverName, cmdEnum);
                break;
            case jar:
                exeJar(cmdInfoVo, exe, serverName, cmdEnum);
                break;
            case service:
                exeService(cmdInfoVo, exe, serverName, cmdEnum);
                break;
            case bat:
                exeBat(cmdInfoVo, exe, serverName, cmdEnum);
                break;
            case queryFiles:
                List<FilesVo> files = new FileTool().getFiles(cmdInfoVo.getFilePath());
                cmdInfoVo.setReturnStr(JSONUtil.toJsonStr(files));
                exeSuccess(cmdInfoVo);
                break;
            case readFile:
                ArrayDeque<String> lines = new FileTool().lastLine(cmdInfoVo.getFilePath(), cmdInfoVo.getNumber());
                cmdInfoVo.setReturnStr(JSONUtil.toJsonStr(lines));
                exeSuccess(cmdInfoVo);
                break;
            case downloadFile:
                String newPath = new FileTool().yasuoFile(cmdInfoVo.getFilePath());
                File file = new File(newPath);
                String url = clientConfig.getRemoteUrl() + "/jwm/uploadFile";
                HttpResponse response = HttpRequest.post(url)
                        .form("file", file)
                        .form("cmdId", cmdInfoVo.getId())
                        .execute();
                if (response.isOk()) {
                    logger.info("文件上传成功");
                } else {
                    logger.error("文件上传失败");
                    logger.error(response.body());
                }
                break;
            case build:
                ArrayDeque<String> s = exe.exeBuild(cmdInfoVo.getNginxValue());
                cmdInfoVo.setReturnStr(JSONUtil.toJsonStr(s));
                exeSuccess(cmdInfoVo);
                break;
            default:
                break;
        }
    }

    private void exeTomcat(CmdInfoVo cmdInfoVo, ExecuteCommand exe, String serverName, CmdEnum cmdEnum) {
        clientConfig.getTomcatList().stream()
                .filter(vo -> vo.getName().equals(serverName))
                .findFirst()
                .ifPresent(vo -> {
                    switch (cmdEnum) {
                        case start:
                            cmdInfoVo.setReturnStr(exe.exeTomcatStart(vo.getPath()));
                            exeSuccess(cmdInfoVo);
                            break;
                        case stop:
                            cmdInfoVo.setReturnStr(exe.exeTomcatStop(vo.getPath()));
                            exeSuccess(cmdInfoVo);
                            break;
                        case restart:
                            exe.exeTomcatStop(vo.getPath());
                            Util.sleep(5000);
                            cmdInfoVo.setReturnStr(exe.exeTomcatStart(vo.getPath()));
                            exeSuccess(cmdInfoVo);
                            break;
                        default:
                            break;
                    }
                });
    }

    private void exeJar(CmdInfoVo cmdInfoVo, ExecuteCommand exe, String serverName, CmdEnum cmdEnum) {
        clientConfig.getJarList().stream()
                .filter(vo -> vo.getName().equals(serverName))
                .findFirst()
                .ifPresent(vo -> {
                    switch (cmdEnum) {
                        case restart:
                            cmdInfoVo.setReturnStr(exe.exeBatRestart(vo.getPath(), vo.getBat()));
                            exeSuccess(cmdInfoVo);
                            break;
                        default:
                            break;
                    }
                });
    }

    private void exeService(CmdInfoVo cmdInfoVo, ExecuteCommand exe, String serverName, CmdEnum cmdEnum) {
        clientConfig.getServiceList().stream()
                .filter(vo -> vo.getName().equals(serverName))
                .findFirst()
                .ifPresent(vo -> {
                    switch (cmdEnum) {
                        case start:
                            cmdInfoVo.setReturnStr(exe.exeServiceStart(vo.getServerName()));
                            exeSuccess(cmdInfoVo);
                            break;
                        case stop:
                            cmdInfoVo.setReturnStr(exe.exeServiceStop(vo.getServerName()));
                            exeSuccess(cmdInfoVo);
                            break;
                        case restart:
                            exe.exeServiceStop(vo.getServerName());
                            Util.sleep(5000);
                            cmdInfoVo.setReturnStr(exe.exeServiceStart(vo.getServerName()));
                            exeSuccess(cmdInfoVo);
                            break;
                        default:
                            break;
                    }
                });
    }

    private void exeBat(CmdInfoVo cmdInfoVo, ExecuteCommand exe, String serverName, CmdEnum cmdEnum) {
        clientConfig.getBatList().stream()
                .filter(vo -> vo.getName().equals(serverName))
                .findFirst()
                .ifPresent(vo -> {
                    switch (cmdEnum) {
                        case restart:
                            cmdInfoVo.setReturnStr(exe.exeBatRestart(vo.getPath(), vo.getBat()));
                            exeSuccess(cmdInfoVo);
                            break;
                        case nginxQuery:
                            cmdInfoVo.setReturnStr(nginxConfigEditor.getNginxConfig(vo.getPath()));
                            exeSuccess(cmdInfoVo);
                            break;
                        case nginxEdit:
                            cmdInfoVo.setReturnStr(nginxConfigEditor.editNginxConfig(vo.getPath(), cmdInfoVo.getNginxValue()));
                            queryBats(cmdInfoVo.getClientName(), exe);
                            Util.sleep(1000);
                            exeSuccess(cmdInfoVo);
                            break;
                        case nginxReload:
                            cmdInfoVo.setReturnStr(exe.exeNginxReload(vo.getPath()));
                            exeSuccess(cmdInfoVo);
                            break;
                        default:
                            break;
                    }
                });
    }

    private void exeSuccess(CmdInfoVo cmdInfoVo) {
        HttpUtil.post(clientConfig.getRemoteUrl() + "/jwm/cmdSuccess", JSONUtil.toJsonStr(cmdInfoVo));
    }
}
